// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

#region

using Furion.ViewEngine;
using Furion.ViewEngine.Extensions;
using System.IO.Compression;
using System.Text.RegularExpressions;

#endregion

namespace Neto.CodeGeneration;

/// <summary>
///     代码生成帮助类
/// </summary>
[SuppressSniffer]
public class CgHelper
{
    /// <summary>
    ///     默认的模板
    /// </summary>
    public static readonly Dictionary<string, CgTemplet> DefaultTemplets = new()
    {
        ["tpl_Entities"] =
            new CgTemplet
            {
                OutFileName = "{EntityName}.cs",
                SaveRelativeDirectory = "Entities",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.tpl_Entities.txt"
            },
        ["tpl_Api_Service"] =
            new CgTemplet
            {
                OutFileName = "{ApiArea}{EntityName}Service.cs",
                SaveRelativeDirectory = "{ApiArea}\\{EntityName}",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_Service.txt"
            },
        ["tpl_Api_Input"] =
            new CgTemplet
            {
                OutFileName = "{ApiArea}{EntityName}Input.cs",
                SaveRelativeDirectory = "{ApiArea}\\{EntityName}\\Dto",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_Input.txt"
            },
        ["tpl_Api_Out"] =
            new CgTemplet
            {
                OutFileName = "{ApiArea}{EntityName}Out.cs",
                SaveRelativeDirectory = "{ApiArea}\\{EntityName}\\Dto",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_Out.txt"
            },
        ["tpl_Api_Query"] =
            new CgTemplet
            {
                OutFileName = "{ApiArea}{EntityName}Query.cs",
                SaveRelativeDirectory = "{ApiArea}\\{EntityName}\\Dto",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_Query.txt"
            },
        ["tpl_Api_MapperInput"] =
            new CgTemplet
            {
                OutFileName = "{ApiArea}{EntityName}MapperInput.cs",
                SaveRelativeDirectory = "{ApiArea}\\{EntityName}",
                ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_MapperInput.txt"
            },
        ["tpl_Api_MapperOut"] = new CgTemplet
        {
            OutFileName = "{ApiArea}{EntityName}MapperOut.cs",
            SaveRelativeDirectory = "{ApiArea}\\{EntityName}",
            ResourceFileName = "Neto.Utils.CodeGeneration.Templets.Entities.API.tpl_Api_MapperOut.txt"
        }
    };

    /// <summary>
    ///     日志
    /// </summary>
    static LogXFactory _logger = LogX.GetLogger("CodeGeneration");

    // //读取数据库表
    //  internal static List<DbTableInfo> GetDbTableInfos(string dbTableName, string dbConn, string dbType, string dbSchema,
    //      string dbTablePrefixReplace, string dbTableReplace, string dbTableNoPrefix,
    //      SqlSugarDbSettingOptions sqlSugarDbSettingOptions)
    //  {
    //  }
    //  
    //  //获取默认代码生成模板
    //  internal static string GetDefaultTemplate(string templateName)
    //  {
    //      
    //  }

    //用sqslugar读取数据库表信息，然后遍历模板生成代码

    /// <summary>
    ///     生成代码【使用自定义模板】
    /// </summary>
    /// <param name="cgConfig"></param>
    /// <param name="templetInputs"></param>
    public static void GenerateCode(CgConfig cgConfig, List<CgTempletInput> templetInputs)
    {
        var templets = templetInputs.Adapt<List<CgTemplet>>();

        GenerateCodeInternal(cgConfig, templets);
    }


    /// <summary>
    ///     生成代码【使用Neto自带默认模板】
    /// </summary>
    /// <param name="cgConfig"></param>
    public static void GenerateCode(CgConfig cgConfig)
    {
        //初始化默认模板
        InitDefaultTemplet();

        GenerateCodeInternal(cgConfig, DefaultTemplets.Values.ToList());
    }

    /// <summary>
    ///     生成代码【使用Neto自带默认模板】
    /// </summary>
    /// <param name="cgConfig"></param>
    private static void GenerateCodeInternal(CgConfig cgConfig, List<CgTemplet> templetInputs)
    {
        var strLog = new StringBuilder();
        strLog.AppendLine($"Path.AltDirectorySeparatorChar:{Path.AltDirectorySeparatorChar}");
        strLog.AppendLine($"Path.DirectorySeparatorChar:{Path.DirectorySeparatorChar}");
        strLog.AppendLine(
            $"配置：{cgConfig.ToJson().JsonFormat()}\r\n模板名：{templetInputs.Select(o => o.TemplateName).ToJson()}");

        var cgTableInfos = GetCgTableInfos(cgConfig);

        var rootPath = App.HostEnvironment.ContentRootPath.TrimEnd('\\');
        strLog.AppendLine($"rootPath:  {rootPath}");

        _logger.Debug(strLog.ToString());

        //获取配置字典
        var cgConfigDics = cgConfig.GetDictionary();

        //
        foreach (var cgTableInfo in cgTableInfos)
        {
            strLog.Clear();
            strLog.AppendLine($"数据库表:  {cgTableInfo.DbTableName} 实体名：{cgTableInfo.EntityName}");
            //if (cgTableInfo.EntityName != "SysLog") continue; 
            var dics = cgTableInfo.GetDictionary();
            foreach (var tpTempletItem in templetInputs)
            {
                try
                {
                    var resultContent = RunCompile(tpTempletItem, cgTableInfo, strLog);

                    var fileName = $"{StrReplace(tpTempletItem.OutFileName, dics, cgConfigDics)}";
                    var fullFileName = rootPath.TrimEnd('\\') +
                                       $"\\GenerateCodes\\{cgConfig.SaveDirectory}\\{StrReplace(tpTempletItem.SaveRelativeDirectory, dics, cgConfigDics)}\\{fileName}"
                                           .ToOsDirectory();

                    strLog.AppendLine($"保存文件：{fileName} 完整路径：{fullFileName}");

                    SaveFile(fullFileName, resultContent);
                }
                catch (Exception e)
                {
                    //throw;
                    _logger.Error($"{strLog.ToString()}\r\n异常：{e.ToStringEx()}");
                }
            }

            _logger.Debug(strLog.ToString());
        }

        //把root/GenerateCodes 目录下的文件夹和文件 压缩为压缩包
        //CompressGenerateCodes(rootPath);
    }


    /// <summary>
    ///     运行编译
    /// </summary>
    /// <param name="cgTemplet"></param>
    /// <param name="tableinfo"></param>
    /// <returns></returns>
    private static string RunCompile(CgTemplet cgTemplet, CgTableInfo tableinfo, StringBuilder strLog)
    {
        var result = "";
        //var _logger = LogX.GetLogger("CodeGeneration");
        try
        {
            result = cgTemplet.TemplateContent.RunCompileFromCached(tableinfo, builderAction: builder =>
                //result = _viewEngine.RunCompileFromCached<CG_CodeTableInfo>(templatecontent, tableinfo, builderAction: builder =>
            {
                //bulder.AddAssemblyReferenceByName("Microsoft.AspNetCore.Mvc.Rendering");
                //builder.AddAssemblyReferenceByName("System.Linq.Dynamic.Core");
                //builder.AddAssemblyReferenceByName("System.Linq.Expressions");
                //builder.AddAssemblyReferenceByName("System.Collections");
                //builder.AddAssemblyReferenceByName("System.Linq");
                builder.AddAssemblyReferenceByName("System.Linq.Dynamic.Core");
                builder.AddAssemblyReferenceByName("Neto.Utils");
                builder.AddAssemblyReferenceByName("SqlSugar");

                builder.AddUsing("System.Linq.Dynamic.Core");
                builder.AddUsing("Neto.Utils");
                builder.AddUsing("SqlSugar");
                //builder.AddUsing("System.Linq.Expressions");
                //builder.AddUsing("System.Collections");
            });
        }

        catch (Exception ex)
        {
            strLog.AppendLine($"模板：{cgTemplet.TemplateName}");
            strLog.AppendLine($"异常：{ex.ToStringEx()}");
            strLog.AppendLine();
            strLog.AppendLine("########## 【内部异常开始】 ##############");

            if (ex.InnerException is ViewEngineTemplateException)
            {
                var innerException = ex.InnerException as ViewEngineTemplateException;
                strLog.AppendLine("错误列表：");
                strLog.AppendLine(innerException?.Errors.ToJson().JsonFormat());
                strLog.AppendLine("\r\n消息：");
                strLog.AppendLine(innerException?.Message);
                strLog.AppendLine("\r\n编译后的模板：");
                strLog.AppendLine(innerException?.GeneratedCode);
            }

            strLog.AppendLine("########## 【内部异常结束】 ##############");


            //strLog.AppendLine(ex.InnerException?)

            _logger.Error(strLog.ToString());
            //throw;
        }

        return result;
    }


    /// <summary>
    ///     获取所有表信息
    /// </summary>
    /// <param name="cgConfig"></param>
    /// <returns></returns>
    public static List<CgTableInfo> GetCgTableInfos(CgConfig cgConfig)
    {
        ISqlSugarClient _db = SqlSugarHelper.Db;
        if (!string.IsNullOrWhiteSpace(cgConfig.DbConn))
        {
            var conn = new ConnectionConfig
            {
                DbType = cgConfig.DbType, //.ToEnum<DbType>(),
                ConfigId = "Abc_GenerateCode", //设置库的唯一标识
                IsAutoCloseConnection = true,
                ConnectionString = cgConfig.DbConn.UrlDecode().HtmlDecode()
            };
            SqlSugarHelper.Db.AddOrUpdateConnection(conn);

            _db = SqlSugarHelper.Db.ToTenant("Abc_GenerateCode");
        }


        var tempTableInfos = new List<CgTableInfo>();

        //数据库表名，，，保证去除分表后不重复
        var tempDbTableNames = new Dictionary<string, string>();

        //所有的表
        var codetypes = CgCodeInternal.GetCodeTypes();

        //数据库配置选项
        var sqlSugarDbSettingOptions = AppX.GetConfig<SqlSugarDbSettingOptions>(isNullDefaultNew: true);
        // 读取数据库表
        var dbTables = _db.DbMaintenance.GetTableInfoList(false); //true 走缓存 false不走缓存
        //遍历所有数据库表
        foreach (var table in dbTables)
        {
            //Console.WriteLine(table.Description);//输出表信息

            //获取列信息
            //var columns=db.DbMaintenance.GetColumnInfosByTableName("表名",false);

            var tableinfo = new CgTableInfo();
            tableinfo.Config = cgConfig;
            tableinfo.DbTableName = table.Name;
            tableinfo.Description = table.Description;

            #region 处理分表的情况，去除掉分表的表名后缀日期部分

            var tableName = table.Name;
            var matchesPattern = Regex.IsMatch(table.Name, "(_[0-9]{8})");
            if (matchesPattern)
            {
                var result = Regex.Replace(table.Name, "(_[0-9]{8})", "");
                tableName = result;
            }

            //如果已经存在，则跳过
            if (!tempDbTableNames.TryAdd(tableName, tableName)) continue;

            #endregion 处理分表的情况，去除掉分表的表名后缀日期部分

            tableinfo.ClassName = CgCodeInternal.GetCsharpName(tableName, CsharpNameType.类名, sqlSugarDbSettingOptions);

            //实体名称，可能包含前缀，根据替换定
            tableinfo.EntityName = CgCodeInternal.GetDbTableReplace(tableinfo.ClassName, cgConfig.DbTableReplace);
            //没有前缀的实体名称
            tableinfo.NoPrefixEntityName =
                CgCodeInternal.GetDbTableNoPrefixEntity(tableinfo.ClassName, cgConfig.DbTableNoPrefix);

            //传入的参数表名，一定是数据库表名
            var dbColumnInfos = _db.DbMaintenance.GetColumnInfosByTableName(table.Name, false);
            foreach (var columnInfo in dbColumnInfos)
            {
                var typeInfo = CgCodeInternal.GetEntityType(codetypes, columnInfo, _db.CurrentConnectionConfig.DbType);

                var column = columnInfo.Adapt<CgTableColumnsInfo>();
                //column.SqlSugarDbColumnInfo = columnInfo;
                column.Description = columnInfo.ColumnDescription;

                column.ClassProperName = CgCodeInternal.GetCsharpName(columnInfo.DbColumnName, CsharpNameType.属性名,
                    sqlSugarDbSettingOptions);
                column.CodeType = typeInfo.CodeType.Name;
                column.Length = typeInfo.DbTypeInfo.Length;
                column.DecimalDigits = typeInfo.DbTypeInfo.DecimalDigits;


                var codeType = codetypes.First(it => it.Name == column.CodeType);
                column.IsSpecialType = CgCodeInternal.IsSpecialType(column);
                column.Type = CgCodeInternal.IsSpecialType(column)
                    ? CgCodeInternal.GetType(column)
                    : codeType.CSharepType;
                tableinfo.ColumnsInfos.Add(column);
            }

            tempTableInfos.Add(tableinfo);
        }


        return tempTableInfos;
    }

    /// <summary>
    ///     初始化默认模板
    /// </summary>
    public static void InitDefaultTemplet()
    {
        var assembly = Assembly.GetExecutingAssembly();

        foreach (var item in DefaultTemplets)
        {
            //var a = _assembly.GetManifestResourceInfo(this.ResourceFileName);

            //var stream = _assembly.GetManifestResourceStream("Neto.Utils.Db.SqlSugar.CodeGeneration.Templets.Entities.tpl_Entities.txt");
            var stream = assembly.GetManifestResourceStream(item.Value.ResourceFileName);
            if (stream == null) continue;
            var reader = new StreamReader(stream);
            item.Value.TemplateContent = reader.ReadToEnd();
        }
    }


    /// <summary>
    ///     压缩生成代码
    /// </summary>
    /// <param name="rootPath"></param>
    public static void CompressGenerateCodes(string rootPath)
    {
        var generateCodesPath = Path.Combine(rootPath, "GenerateCodes");
        if (!Directory.Exists(generateCodesPath))
        {
            Directory.CreateDirectory(generateCodesPath);
        }

        var fileName = $"GenerateCode_{DateTime.Now:yyyyMMddHHmmss}.zip";

        var fullFileName = Path.Combine(generateCodesPath, fileName);

        // 如果输出 ZIP 文件已存在，则先删除
        if (File.Exists(fullFileName))
        {
            File.Delete(fullFileName);
        }

        // 创建 ZIP 压缩包
        ZipFile.CreateFromDirectory(generateCodesPath, fileName);

        Console.WriteLine($"压缩完成: {fullFileName}");
    }

    /// <summary>
    ///     字符串替换
    /// </summary>
    /// <param name="str"></param>
    /// <param name="replaces"></param>
    /// <param name="cgConfigDics"></param>
    /// <returns></returns>
    private static string StrReplace(string str, Dictionary<string, string> replaces,
        Dictionary<string, string> cgConfigDics)
    {
        if (string.IsNullOrEmpty(str)) return str;
        foreach (var item in replaces)
        {
            var oldValue = item.Key;
            var newValue = item.Value;
            str = str.Replace($"{{{oldValue}}}", newValue);
        }

        foreach (var item in cgConfigDics)
        {
            var oldValue = item.Key;
            var newValue = item.Value;
            str = str.Replace($"{{{oldValue}}}", newValue);
        }

        return str;
    }

    /// <summary>
    ///     保存文件
    /// </summary>
    /// <param name="fullFileName"></param>
    /// <param name="fileContent"></param>
    private static void SaveFile(string fullFileName, string fileContent)
    {
        fullFileName = fullFileName.ToOsDirectory();
        //创建文件
        if (FileHelper.IsExistFile(fullFileName))
        {
            var isDelete = FileHelper.TryDeleteFile(fullFileName);
            if (isDelete) FileHelper.CreateFile(fullFileName, fileContent, Encoding.UTF8);
        }
        else
            FileHelper.CreateFile(fullFileName, fileContent, Encoding.UTF8);
    }
}